#ifndef __SMEG_EDITOR_FRAME_HPP__
#define __SMEG_EDITOR_FRAME_HPP__

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <wx/propgrid/propgrid.h>

#include <fstream>
#include <vector>
#include <map>

#include "SmegEditor_gui.h"

#include "../Canvas/SmegEditorCanvas.hpp"

#include "../ObjectHandler/ObjectHandlers.h"

namespace smeg {
    namespace light {
        class Light;
    }
}

namespace tinyxml2 {
    class XMLElement;
}

////////////////////////////////////////////////////////////
/// Defines
////////////////////////////////////////////////////////////
#define wxID_OBJECT_PROPERTY 2001

////////////////////////////////////////////////////////////
/// SmegEditorFrame is the main frame of the level editor
////////////////////////////////////////////////////////////
class SmegEditorFrame : public SmegEditorFrameGUI
{
	public:
        ////////////////////////////////////////////////////////////
        /// Construct the SmegEditorFrame
        ///
        /// \param _pParent :   Parent of the frame
        ///
        ////////////////////////////////////////////////////////////
		                    SmegEditorFrame( wxWindow* _pParent );

        ////////////////////////////////////////////////////////////
        /// Destructor
        ///
        ////////////////////////////////////////////////////////////
		virtual             ~SmegEditorFrame();

        ////////////////////////////////////////////////////////////
        /// Call UpdateAndRender on the handlers
        ///
        ////////////////////////////////////////////////////////////
        void                UpdateAndRenderHandlers();
                        
        ////////////////////////////////////////////////////////////
        /// Load all properties of all components
        ///
        /// \param _Filename :  File where all the properties are
        ///                     stored
        ///
        /// \return :           True if properties are loaded
        ///                     False otherwise
        ///
        ////////////////////////////////////////////////////////////
        bool                LoadAllProperties( const wxString& _Filename );
                        
        ////////////////////////////////////////////////////////////
        /// The selected object was moved
        ///
        /// \param _NewPos :  New position of the object
        ///
        ////////////////////////////////////////////////////////////
        void                OnObjectChangePosition( const sf::Vector2f& _NewPos );
                        
        ////////////////////////////////////////////////////////////
        /// The selected object was rotated
        ///
        /// \param _fNewRot :  New rotation of the object
        ///
        ////////////////////////////////////////////////////////////
        void                OnObjectChangeRotation( float _fNewRot );
                        
        ////////////////////////////////////////////////////////////
        /// Set the selected object as to be copied
        ///
        ////////////////////////////////////////////////////////////
        void                CopySelected();
                        
        ////////////////////////////////////////////////////////////
        /// Set the selected object as to be cut
        ///
        ////////////////////////////////////////////////////////////
        void                CutSelected();
                        
        ////////////////////////////////////////////////////////////
        /// Paste the selected object
        ///
        ////////////////////////////////////////////////////////////
        void                PasteSelected();
                        
        ////////////////////////////////////////////////////////////
        /// Set if the project as been modified or is up to date
        ///
        /// \param _bIsModified : true if the project has been modifier, false if it is up to date
        ///
        ////////////////////////////////////////////////////////////
        void                SetProjectModified( bool _bIsModified );
        
        ////////////////////////////////////////////////////////////
        /// Get all the handlers that are of the type T
        ///
        /// \param _OutVector : vector to put the handlers
        ///
        ////////////////////////////////////////////////////////////
        template <typename T>
        void                GetHandlers( std::vector<T*>& _OutVector );
        
        ////////////////////////////////////////////////////////////
        /// Get a handler corresponding to the given name
        ///
        /// \param _Name :  Name to look for
        ///
        ////////////////////////////////////////////////////////////
        BaseObjectHandler*  GetHandlerByName( const wxString& _Name );
		
    protected:
        struct wxAttribute {
            wxString                        m_Name;
            wxString                        m_Value;

            wxAttribute( const wxString& _name, const wxString& _value ) :
                m_Name( _name ),
                m_Value( _value ) {}
        };
        struct SmegProperty {
            wxString                        m_ClassName;
            wxString                        m_Label;
            wxString                        m_Name;
            wxString                        m_Value;
            wxString                        m_Set;
            wxArrayString                   m_Choices;
            std::vector< wxAttribute >      m_Attributes;
        };
        
        typedef std::vector< SmegProperty >                         td_propertyVector;
        typedef std::map< wxString, td_propertyVector >             td_categoryMap;
        typedef std::map< wxString, td_categoryMap >                td_objectPropMap;

        typedef std::map< wxString, BaseObjectHandler* >            td_handlerMap;

        ////////////////////////////////////////////////////////////
        /// Parse a property
        ///
        /// \param _pPropertyElement :   Property to parse
        /// \param _PropertyToFill :    Property to fill
        ///
        ////////////////////////////////////////////////////////////
        void                ParseProperty( tinyxml2::XMLElement* _pPropertyElement, SmegProperty& _PropertyToFill );
                        
        ////////////////////////////////////////////////////////////
        /// Parse a property
        ///
        /// \param _pCategoryElement :   Category to parse
        /// \param _CategoryMap :       Category to fill
        ///
        ////////////////////////////////////////////////////////////
        void                ParseCategory( tinyxml2::XMLElement* _pCategoryElement, SmegEditorFrame::td_categoryMap& _CategoryMap );
                        
        ////////////////////////////////////////////////////////////
        /// Clear the handler and the properties
        ///
        ////////////////////////////////////////////////////////////
        void                ClearSelection();
                        
        ////////////////////////////////////////////////////////////
        /// Clear the project
        ///
        ////////////////////////////////////////////////////////////
        void                ClearProject();
                        
        ////////////////////////////////////////////////////////////
        /// Ask the user if he wants to save the project
        ///
        /// \return         true if the user saved or not the project, false if the user cancelled
        ///
        ////////////////////////////////////////////////////////////
        bool                ProjectModifiedDialog();
                        
        ////////////////////////////////////////////////////////////
        /// Get the name of a project item (the name before the ':')
        ///
        /// \param _Item :      The id of the item we want the name
        ///
        ////////////////////////////////////////////////////////////
        wxString            GetProjectItemName( wxTreeItemId _Item ); 
                        
        ////////////////////////////////////////////////////////////
        /// Return an available name from the given base name
        ///
        /// \param _BaseName :   Base name to create an available one from
        ///
        ////////////////////////////////////////////////////////////
        wxString            GetAvailableName( const wxString& _BaseName );
                        
        ////////////////////////////////////////////////////////////
        /// Add a new handler
        ///
        /// \param _pHandler :   New handler to add
        /// \param _Parent :    Parent where to add the handler
        ///
        ////////////////////////////////////////////////////////////
        void                AddHandler( BaseObjectHandler* _pHandler, wxTreeItemId _Parent );
                        
        ////////////////////////////////////////////////////////////
        /// Remove a handler
        ///
        /// \param _pHandler :           Handler to remove
        /// \param _pHandlerItem :       Handler item to remove, if NULL, look for it in the tree
        ///
        ////////////////////////////////////////////////////////////
        void                RemoveHandler( BaseObjectHandler* _pHandler, wxTreeItemId* _pHandlerItem = NULL );
                        
        ////////////////////////////////////////////////////////////
        /// Remove handlers
        ///
        /// \param _Handlers :   Handlers to remove
        ///
        ////////////////////////////////////////////////////////////
        void                RemoveHandlers( const std::vector< BaseObjectHandler* >& _Handlers );
                        
        ////////////////////////////////////////////////////////////
        /// Fill the PropertyGrid depending on the object given
        ///
        /// \param _category :      Category to fill
        /// \param _pPropertyVec :   Vector of property
        /// \param _pHandler :   Handler that have some already defined property (NULL if we want the default)
        ///
        /// \return :           True if properties are filled
        ///                     False otherwise
        ///
        ////////////////////////////////////////////////////////////
        bool                FillCategoryProperties( const wxString& _CategoryName, td_propertyVector* _pPropertyVec, BaseObjectHandler* _pHandler = NULL );
                        
        ////////////////////////////////////////////////////////////
        /// Fill the PropertyGrid depending on the object given
        ///
        /// \param _Object :    Object we want the properties
        /// \param _pHandler :   Handler that have some already defined property (NULL if we want the default)
        ///
        /// \return :           True if properties are filled
        ///                     False otherwise
        ///
        ////////////////////////////////////////////////////////////
        bool                FillProperties( const wxString& _Object = wxEmptyString, BaseObjectHandler* _pHandler = NULL );
        
        ////////////////////////////////////////////////////////////
        /// Move/Copy Children from an item to another
        ///
        /// \param _OldParent :  Old parent of the children
        /// \param _NewParent :  New parent of the children
        /// \param _bRemoveOld : If true : move the children
        ///                     If false, copy the children
        ///
        ////////////////////////////////////////////////////////////
        void                MoveProjectTreeChildren( wxTreeItemId _OldParent, wxTreeItemId _NewParent, bool _bRemoveOld = true );
                        
        ////////////////////////////////////////////////////////////
        /// Remove a project item with the specified text
        ///
        /// \param _ItemText :          Item with this text will be removed
        /// \param _Item :              Item to begin the search
        /// \param _bRemoveChildren :    If true, item with children will have its children removed,
        ///                             Otherwise, children will be move to the upper level
        ///
        ////////////////////////////////////////////////////////////
        void                RemoveProjectTreeItemsWithText( const wxString& _ItemText, wxTreeItemId _Item, bool _bRemoveChildren = true );
                        
        ////////////////////////////////////////////////////////////
        /// Remove children
        ///
        /// \param _OldParent :  Old parent of the children
        /// \param _bRemoveOld : If true, remove the parent too, otherwise don't
        ///
        ////////////////////////////////////////////////////////////
        void                RemoveProjectTreeChildren( wxTreeItemId _OldParent, bool _bRemoveOld = true );
        
        ////////////////////////////////////////////////////////////
        /// Parse a project item from a file
        ///
        /// \param _pElement :      Element to parse
        /// \param _Parent :    Parent item of the element
        ///
        /// \return Whether the parse was successful or not
        ///
        ////////////////////////////////////////////////////////////
        bool                ParseProjectElement( tinyxml2::XMLElement* _pElement, wxTreeItemId _Parent );
        
        ////////////////////////////////////////////////////////////
        /// Save a project item to a file
        ///
        /// \param _File :      File to save the item
        /// \param _Item :      Item to save
        /// \param _Tab :       Tab string to have a beautiful XML
        ///
        ////////////////////////////////////////////////////////////
        void                SaveItemToProjectFile( std::ofstream& _File, wxTreeItemId _Item, const wxString& _Tab = wxT( "\t" ) );
        
        ////////////////////////////////////////////////////////////
        /// Save a project to a file
        ///
        /// \param _bForceDialog :  True if we want to save as, false if we overwrite the existing file (if any, otherwise show save dialog)
        ///
        ////////////////////////////////////////////////////////////
        void                SaveProject( bool _bForceDialog = false );
        
        ////////////////////////////////////////////////////////////
        /// Setup the menus items after a selection
        ///
        ////////////////////////////////////////////////////////////
        void                SetupMenuAfterSelection();

        ////////////////////////////////////////////////////////////
		/// Event handlers
        ///
        ////////////////////////////////////////////////////////////
		virtual void        OnNewProject( wxCommandEvent& event );
		virtual void        OnOpenProject( wxCommandEvent& event );
		virtual void        OnSaveProject( wxCommandEvent& event );
		virtual void        OnSaveProjectAs( wxCommandEvent& event );

        void                OnClose( wxCloseEvent& event );
		virtual void        OnQuit( wxCommandEvent& event );
		virtual void        OnAbout( wxCommandEvent& event );
		void                OnSize( wxSizeEvent& event );
        
        // Project Ctrl
		virtual void        OnProjectItemBeginDrag( wxTreeEvent& event );
		virtual void        OnProjectItemEndDrag( wxTreeEvent& event );

		virtual void        OnProjectItemAddGroup( wxCommandEvent& event );
		virtual void        OnProjectItemRemove( wxCommandEvent& event );
        virtual void        OnProjectItemSubMenu( wxTreeEvent& event );
		virtual void        OnProjectItemSelected( wxTreeEvent& event );

        // Component Ctrl
        virtual void        OnComponentSelected( wxTreeEvent& event );

        void                OnPropertyChanged( wxPropertyGridEvent& event );
        void                OnPropertyDoubleClick( wxPropertyGridEvent& event );

        // Copy/Cut/Paste Menu Events
		virtual void OnCopySelected( wxCommandEvent& event )    { CopySelected();   event.Skip(); }
		virtual void OnCutSelected( wxCommandEvent& event )     { CutSelected();    event.Skip(); }
		virtual void OnPasteSelected( wxCommandEvent& event )   { PasteSelected();  event.Skip(); }

        /******************* Protected members ********************/
        td_categoryMap*                     m_pCurrentObjectProp;
        td_objectPropMap                    m_ObjectPropMap;

        SmegEditorCanvas*                   m_pEditorCanvas;
        wxPropertyGrid*                     m_pPropertyGrid;
        
        td_handlerMap                       m_AllHandlers;
        std::vector< BaseObjectHandler* >   m_CurrentHandlers;
        
        bool                                m_bIsCopy;           // If true, copy the object, if false, cut it
        std::vector< BaseObjectHandler* >   m_CopyCutHandlers;
        
        wxTreeItemId                        m_ProjectItemSelected;

        wxFileName                          m_CurrentProjectFilename;
        bool                                m_bProjectModified;

        wxImageList                         m_ComponentsIcons;

        bool                                m_bDoesUpdateHandlers;
};

////////////////////////////////////////////////////////////
/// Get all the handlers that are of the type T
////////////////////////////////////////////////////////////
template <typename T>
void SmegEditorFrame::GetHandlers( std::vector<T*>& _OutVector ) {
    T* pCurHandler;
    for ( td_handlerMap::iterator it = m_AllHandlers.begin();
        it != m_AllHandlers.end();
        ++it ) {
        pCurHandler = dynamic_cast<T*>( it->second );
        if ( pCurHandler ) {
            _OutVector.push_back( pCurHandler );
        }
    }
}

#endif // __SMEG_EDITOR_FRAME_HPP__
